Skip to content

Bun.write(file, file): wait for a non-blocking fd instead of rejecting with EAGAIN - #46858

Open
robobun wants to merge 3 commits into
mainfrom
robobun/f6f6cac7/bun-write-copyfile-eagain
Open

robobun wants to merge 3 commits into
mainfrom
robobun/f6f6cac7/bun-write-copyfile-eagain

Conversation

@robobun

@robobun robobun commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • await Bun.write(Bun.stdout, Bun.file(path)) rejects with EAGAIN (syscall sendfile) after 65536 bytes when fd 1 is a non-blocking pipe. Any use of process.stdout sets that flag (src/io/openForWriting.rs:157).
  • CopyFile, the work-pool job behind Bun.write(file, file), has no case for EAGAIN: do_copy_file_range (src/runtime/webcore/blob/copy_file.rs:477), the read/write loops (:990, node_fs.rs:4883).

Fix

  • bun_sys::read_retrying and write_retrying wait on EAGAIN, then repeat the call. read_write_loop_capped and the node:fs loop call them.
  • On EAGAIN from sendfile or splice the copy continues in that loop, as in dylan-conway's 90e0621 (console: output goes behind what process.stdout/stderr have buffered, as in Node #37128).
  • Verified: test/js/bun/io/bun-write.test.js (7 new tests, all fail on main). Also process-stdio.test.ts, fs/cp.test.ts, shell cp.test.ts, source lints, rust:check-all.
  • Self-reviewed: 30 concerns raised, 28 addressed. Rejected: a tty test, and a guard for a socket that poll cannot wait for.

Background

  • O_NONBLOCK belongs to the open file description. Every fd on the same pipe shares it.
  • The wait is poll. macOS uses select, because poll there does not wake when the other end of a named pipe closes.
  • The first revision kept sendfile and splice after a wait. Review found two busy loops in that wait.

Downsides

  • A waiting copy holds its pool thread, as a blocking fd does. worker.terminate() then takes 1.68 s (main: 0 ms, the copy failed).
  • The file can land inside data that process.stdout still holds: 14 of 20 runs (main: 6 of 20, and all 20 reject).
  • After the first EAGAIN a copy costs 370 ms of CPU per GiB, against 266 ms with sendfile. Without EAGAIN the syscalls are the same.
Notes

Repro (Linux, bun 1.4.3-canary.1+367d939d9 and main 179664e, 3 of 3 runs each)

head -c 1048576 /dev/zero | tr '\0' 'x' > big.bin
cat > cat.mjs <<'EOF'
process.stdout.isTTY;
try {
  const n = await Bun.write(Bun.stdout, Bun.file("big.bin"));
  process.stderr.write("resolved " + n + "\n");
} catch (e) {
  process.stderr.write(`rejected ${e.code} (${e.syscall})\n`);
}
EOF
bun cat.mjs | wc -c

main prints rejected EAGAIN (sendfile) and 65536. This PR prints resolved 1048576 and 1048576.

Reach. No issue reports this: searches for EAGAIN sendfile, EAGAIN splice and EAGAIN "Bun.write" find 0 issues. It was found during work on #41685. It is deterministic for a file above 65536 bytes and a pipe.

Who sets the flag, and who clears it

  • A use of process.stdout or process.stderr in the script or in a dependency, for example a read of isTTY.
  • A Bun that ran before on the same pipe. Bun does not clear the flag at exit (process: restore O_NONBLOCK on inherited stdio at exit #33827 proposed that and is closed). ( bun -e "process.stdout.isTTY"; bun cat.mjs ) | wc -c fails in the second process on main.
  • One socket that is fd 0 and fd 1, as inetd and socat EXEC: start a server. The use of process.stdout makes stdin non-blocking too.
  • Bun.spawn makes its own end of an extra "pipe" non-blocking (proc.stdio[3]).
  • A spawn clears the flag on the fds that become fd 0, 1 and 2 of the child (src/spawn_sys/spawn_process.rs:793, since process.stdout/stderr: clear O_NONBLOCK for spawned children, don't drop console output on EAGAIN #43868). A copy after a spawn with an inherited stdout resolves on main.

How the copy waits

  • read_retrying and write_retrying (src/sys/lib.rs) make one read or write. On EAGAIN, wait_until_readable or wait_until_writable blocks the thread with no timeout, and the call repeats. If the wait fails, the helper returns the EAGAIN.
  • The wait is poll. On macOS it is select$DARWIN_EXTSN$NOCANCEL, which has no FD_SETSIZE limit. write_all_to_stdio (process.stdout/stderr: clear O_NONBLOCK for spawned children, don't drop console output on EAGAIN #43868) calls the same wait in place of its own poll.
  • read_write_loop_capped copies into an fd destination. NodeFS::copy_file_using_read_write_loop copies into a path destination. Both call the helpers for each read and write.
  • do_copy_file_range: EAGAIN from sendfile or splice returns fallback_read_write(remain, unknown_size, &mut total_written), as ENOSYS and EXDEV do. The loop continues at the byte where the kernel copy stopped. copy_file_range has no such arm: it runs between two regular files.
  • The node:fs loop also serves fs.copyFile, fs.cp and Bun Shell cp. Its 5 other call sites open both ends from paths after an ISREG check of the source. On Linux a path gives a new blocking description, so those calls cannot get EAGAIN. On macOS and FreeBSD a destination such as /dev/stdout shares the description of fd 1, so there the loop now waits where it failed with EAGAIN. That was not run.

Tests. Each test spawns a child that reports { ready } and then how the promise settled. A child in a pipeline also reports its exit status.

  1. A file to a stdout pipe that fills during the copy. Linux: sendfile copies 65536 bytes, fails with EAGAIN, and the loop continues at that byte. The child measures its CPU time for 1 s while the copy waits.
  2. A stdin pipe to a stdout pipe that fills during the copy. Linux: the same for splice.
  3. Linux only: a stdin pipe with no data yet, after fd 1 became non-blocking. splice fails with EAGAIN at byte 0.
  4. A file to a full stdout socket.
  5. A FIFO source that the child opens with O_NONBLOCK on an fd above 1024. The child measures its CPU time while the copy waits for data. The writer closes and the copy resolves.
  6. A full FIFO destination. A reader takes a part and leaves, and the copy rejects with EPIPE write.
  7. Linux only: one socket is stdin and stdout of the child, and the child uses process.stdout. Bun.write(path, Bun.stdin) gets three pieces, each after the one before it is in the file, and resolves with all 12 bytes. The file at the path has 5 bytes and a known size. The second piece ends in the sized part of the node:fs loop, the third in the part after it.

On main all 7 fail: 1 of 1 run of a debug build and 15 of 15 runs of a release build. On this PR all 7 pass in 30 of 30 runs of a release build and in 4 of 4 runs of a debug build. They also pass in 2 of 2 runs with the environment of the ASAN lane.

Each part of the fix breaks a test when it is removed (debug builds):

  • No EAGAIN arm in do_copy_file_range: tests 1 to 6 fail with EAGAIN sendfile or EAGAIN splice.
  • write for write_retrying in read_write_loop_capped: tests 1, 2, 4 and 6 fail with EAGAIN write.
  • read for read_retrying in read_write_loop_capped: test 5 fails with EAGAIN read.
  • read for read_retrying in the node:fs loop: test 7 fails after hel (the sized part) or after hello (the part after it).
  • A wait that returns at once: tests 1 and 5 report 880 and 932 ms of CPU in the 1 s window. A copy that waits uses 5 to 8 ms (release) or 58 to 70 ms (debug).
  • The two write sites of the node:fs loop have no test. They can get EAGAIN only on macOS and FreeBSD, through a path that shares the description of fd 1.

Measurements (release builds, linux x64, main 179664e against this PR)

  • Syscalls of copies that never get EAGAIN, 10 workloads, 3 runs each: 0 of 10 differ. poll and select calls: 0.
    • The workloads: bench/cat/bun.js to a file and to a pipe, and stdin to stdout. The same three with BUN_CONFIG_DISABLE_COPY_FILE_RANGE=1. bench/copyfile/bun.js with and without that variable. 200 calls of fs.copyFileSync. A file to Bun.file(3).
    • The counted calls: sendfile, splice, copy_file_range, read, poll, ppoll, select, pselect6, fcntl, ftruncate, fstat, openat. The write count changes by one between runs on both builds.
  • Instructions that run in user space for one 64 KiB chunk, counted with gdb stepi. read_write_loop_capped: 47 to 77, with 11 to 13 conditional branches. The node:fs loop: 61 to 69, with 11 to 12. Most of the difference is a write that is now a call.
  • do_copy_file_range with sendfile: 33 instructions and 5 conditional branches for each call, on both builds. The two copy_file_range instances have the same size. The sendfile and splice instances grow by 2 bytes or 0.
  • size: text 107,557,538 to 107,558,818 bytes (+1,280).
  • struct CopyFile, its JobContext impl and CopyFile::create have no hunk: no new allocation, queue entry or field.
  • CPU time of the writer for a 1 GiB file to | cat > /dev/null after a use of process.stdout. Medians of 12 interleaved runs, with the range: this PR 370 ms (224 to 709). The first revision, which stays on sendfile: 266 ms (159 to 536). main with a blocking fd 1: 192 ms (148 to 414).
  • A 4 MiB file to a full pipe that a reader then drains 64 KiB at a time. 1 sendfile call fails with EAGAIN. Then 64 read and 64 write calls copy. 42 write calls fail with EAGAIN, and there are 42 poll calls.
  • An idle stdin pipe after the reader of stdout left, for 3 s: 1 splice call, then a read that blocks. 10 to 16 ms of CPU. The copy resolves with 0 when stdin ends.
  • worker.terminate() while a copy in the worker waits for a reader that starts after 2 s. main with a blocking fd 1: it settles after 1.65 s. main with a non-blocking fd 1: after 0 ms, because the copy rejected. This PR with a non-blocking fd 1: after 1.68 s.
  • process.exit() on the main thread does not wait: the process is gone after 336 ms (main with a blocking fd 1: 337 ms).
  • fs.promises.stat behind 12 copies that wait (12 pool threads): it settles when the reader starts, on this PR and on main with a blocking fd 1.
  • process.stdout.write("A".repeat(200000)), then a copy of a 100 KiB file of B:
    • A reader that runs at once, 20 runs: the file is inside the A run in 14 and after it in 6. main rejects 20 of 20, and its B bytes are inside the A run in 6.
    • A reader that starts after 0.3 s, 10 runs: inside in 6, after it in 4. main rejects 10 of 10 and writes no B.
    • The script waits for the callback of process.stdout.write first, 20 runs: in order 20 of 20. main rejects 20 of 20.
  • Tools: a ptrace syscall counter, gdb, objdump, size, nm. The machine has no strace or perf.

Not run here. No macOS or FreeBSD machine was available. cargo check passes for all 12 targets. On macOS, tests 1, 2, 4, 5 and 6 run the select wait, and tests 1 and 5 put the CPU limit on it. Windows is not affected and not run: only POSIX code sets O_NONBLOCK on stdio, and CopyFileWindows is other code.

An idle source whose reader left. The first revision rejected with EPIPE at once, because sendfile looks at the pipe first. Now the copy is in read and waits for input, as cat does. EPIPE comes with the next byte, or the copy resolves when the source ends.

Considered. A park on the io thread, as WriteFile does: every copy pays a job-list entry, and the loops still need a wait for the fds that cannot park. A wait that keeps sendfile and splice (the first revision): it saves the CPU time above and needs a rule for the end to wait for.

Other paths of Bun.write with the same trigger. This PR changes only the file-to-file job.

Still wrong after this PR

History. #38498 (closed in favour of #37128) had the wait and most of these tests. #43868 made the console writer wait in poll on EAGAIN (write_all_to_stdio). #37128 had this fix in commit 90e0621: EAGAIN from sendfile and splice went to the read/write loop, and read_retrying and write_retrying waited in both loops. Commit 9033ecd cut that PR down to the console change. This PR has that shape again. It adds the select wait for macOS and leaves out the EINTR arm, which #45929 owns. Bun.write(Bun.stdout, Bun.file(path).stream()) is a workaround on main: it goes through the stream sink and the event loop.

Questions for @dylan-conway

Overlap. #41685, #46636 and #45929 edit other lines of do_copy_file_range. copy_file.rs merges with each of them without a conflict (git merge-file). The 3-argument fallback_read_write that #41685 and #46636 call is the same.


no test proof · iteration 1 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/io/bun-write.test.js

…g with EAGAIN

The copy loops behind Bun.write(file, file) treated EAGAIN as a failure.
A pipe or socket on fd 1 is non-blocking once process.stdout is used, so
`await Bun.write(Bun.stdout, Bun.file(path))` rejected after a part of
the file.

On EAGAIN the copy now waits until the fd is ready and repeats the same
syscall:

- bun_sys::wait_until_readable / wait_until_writable block in poll, and
  in select on macOS, where poll does not wake when the other end of a
  named pipe closes. write_all_to_stdio uses them too.
- do_copy_file_range (sendfile, splice) asks poll which end is not
  ready, then waits for it. A destination that reports an error gets
  one retry, for the syscall to report it. After that only the source
  ends the wait, because splice does not look at the destination while
  the source is empty.
- read_write_loop_capped waits for the fd whose read or write failed.
@robobun

robobun commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 1:31 PM PT - Oct 25th, 2026

✅ @robobun, your commit 320ef461f3a42077ee47d85d1d5e170f5f796362 passed in Build #132727! 🎉


🧪   To try this PR locally:

bunx bun-pr 46858

That installs a local version of the PR into your bun-46858 executable, so you can run:

bun-46858 --bun

@robobun

robobun commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status

  • Reproduced on bun 1.4.3-canary.1+367d939d9 and on main 179664e, 3 of 3 runs each: process.stdout.isTTY; await Bun.write(Bun.stdout, Bun.file("big.bin")) into | wc -c delivers 65536 of 1048576 bytes and rejects with EAGAIN (syscall sendfile).
  • With this PR the same command resolves with 1048576. The 7 new tests in test/js/bun/io/bun-write.test.js fail on main and pass on this branch.
  • The fix changed shape in 79a1f99: on EAGAIN from sendfile or splice the copy continues in the read/write loop, and the read/write loops wait. The PR description has the reasons and the numbers.

@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 91540f5 and 79a1f99.

Files selected for processing: 4
  • src/runtime/node/node_fs.rs
  • src/runtime/webcore/blob/copy_file.rs
  • src/sys/lib.rs
  • test/js/bun/io/bun-write.test.js

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.

Walkthrough

Unix file-copy paths now wait for readiness and retry after EAGAIN. New Unix helpers use poll or macOS select. Tests cover non-blocking copies through pipes, sockets, and FIFOs.

Changes

File-copy readiness

Layer / File(s) Summary
Unix readiness waits
src/sys/lib.rs
Adds public readability and writability wait helpers. Non-macOS Unix uses poll; macOS uses non-cancellable select. write_all_to_stdio uses the writable wait after EAGAIN.
Retrying file-copy operations
src/sys/lib.rs, src/runtime/node/node_fs.rs, src/runtime/webcore/blob/copy_file.rs, test/js/bun/io/bun-write.test.js
Adds retrying read and write helpers and uses them in NodeFS and blob copy paths. Linux and Android kernel-copy paths fall back after EAGAIN from sendfile or splice, but not copy_file_range. Tests check copy results and errors across non-blocking pipes, sockets, and FIFOs.

Suggested reviewers: jarred-sumner

Priority: Normal

Merge Risk: Minimal · up to 320ef

Merge Risk: ⚪ Minimal · up to 320ef

The reviewed copy paths have no identified issue requiring a fix before merge.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: waiting for non-blocking file descriptors instead of failing with EAGAIN during Bun.write file copies.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description follows the template with both required sections, "What does this PR do?" (Problem, Fix, Background, Downsides) and "How did you verify your code works?" (test file, 7 new tests, faili…
Full details: Description check

Explanation

The description follows the template with both required sections, "What does this PR do?" (Problem, Fix, Background, Downsides) and "How did you verify your code works?" (test file, 7 new tests, failing on main, plus existing suites run). It is detailed and specific, and it names the affected files and functions. The main gap is that it does not verify on macOS or FreeBSD, and it says so. The bot evidence note flags that the platform-specific tests defer to CI. The PR objectives mention nine new tests, while the description says 7, so the count differs and should be reconciled. Verification is therefore substantive but partly unverified for macOS and FreeBSD.

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🔴 src/runtime/webcore/blob/copy_file.rs — pre-existing: a copy from a non-blocking pipe or socket into a path destination still rejects with EAGAIN after this change, while the fd-destination twin now waits. The Bun-opened-path branch at copy_file.rs:1011 (and copy_file.rs:556, copy_file.rs:913 on macOS/FreeBSD) hands the copy to NodeFS::copy_file_using_read_write_loop, whose read at node_fs.rs:4883 returns every error unchanged. Fix: give that loop the same EAGAIN handling as read_write_loop_capped (wait_if_would_block with wait_until_readable/wait_until_writable on read and write), or route the non-regular-source case through read_write_loop_capped, so every CopyFile destination kind waits. [also at: src/runtime/webcore/blob/copy_file.rs:556 - pre-existing: a copy from a non-blocking fd into a path destination still rejects with EAGAIN after this change, same as on the base branch.]

    Why this was flagged

    Trigger: Bun.write("out.bin", Bun.file(fd)) where fd is a FIFO opened with O_NONBLOCK or a non-blocking socket on stdin, with a path destination. On Linux a socket source takes the Sendfile branch at copy_file.rs:850, sendfile fails EINVAL with total_written == 0 and falls to fallback_read_write at copy_file.rs:464, which calls NodeFS::copy_file_using_read_write_loop at copy_file.rs:1011. On macOS a FIFO/socket source with a path destination goes through do_fcopy_file_with_read_write_loop_fallback and reaches the same NodeFS loop at copy_file.rs:556; FreeBSD at copy_file.rs:913. That loop's Syscall::read at node_fs.rs:4883 and its write loop return any Err, including EAGAIN, so the promise rejects with EAGAIN exactly as on the base branch. The new wait_if_would_block helper (copy_file.rs:1029) is only wired into read_write_loop_capped, the fd-destination path, so the safeguard this PR adds does not reach the path-destination twin.

    Verification: pre-existing — the base rejects by the same route. The PR fixes read_write_loop_capped (copy_file.rs:1055-1074) but leaves its twin untouched. Every Path destination routes through NodeFS::copy_file_using_read_write_loop (copy_file.rs:1011, 556, 913), which returns read errors unchanged at node_fs.rs:4883-4891, so EAGAIN from a non-blocking source rejects the promise while the fd-destination twin now waits.

Comment thread src/runtime/webcore/blob/copy_file.rs Resolved
Comment thread test/js/bun/io/bun-write.test.js Resolved
Comment thread src/runtime/webcore/blob/copy_file.rs Resolved
Comment thread src/runtime/webcore/blob/copy_file.rs Resolved
…AIN from sendfile and splice to them

`bun_sys::read_retrying` and `write_retrying` wait for a non-blocking fd
that is not ready, as a blocking fd does. `read_write_loop_capped` and
the node:fs loop call them.

`do_copy_file_range` no longer waits and repeats `sendfile` or `splice`.
On EAGAIN it continues the copy in the read/write loop. This removes
`wait_for_kernel_copy` and its rule for the end to wait for.

The tests cover the continuation after `sendfile` and `splice` copied a
part, both waits with a CPU limit, and a path destination.

Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
@robobun

robobun commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to the review finding outside the diff (a non-blocking source with a path destination).

  • Fixed in 79a1f99 for Linux and FreeBSD. NodeFS::copy_file_using_read_write_loop now calls bun_sys::read_retrying and write_retrying, the same helpers as read_write_loop_capped. A read or a write that fails with EAGAIN waits, then repeats.
  • This is the first of the two fixes that the finding names, and it is what 90e0621 in console: output goes behind what process.stdout/stderr have buffered, as in Node #37128 did. The other callers of that loop (fs.copyFile, fs.cp, Bun Shell cp) open both ends from paths after an ISREG check of the source. On Linux a path gives a new blocking description, so those calls cannot get EAGAIN.
  • New test: a stdin socket to a path, once the same socket on fd 1 is non-blocking (Linux). One socket is fd 0 and fd 1, the child uses process.stdout, and Bun.write(path, Bun.stdin) gets three pieces. main rejects with EAGAIN read after the first piece. The test also fails when either read site of the loop does not wait.
  • macOS does not reach that loop for this input. fcopyfile refuses a source that is not a regular file with ENOTSUP (Apple copyfile.c, "prevent copying on unsupported types"). That is Writing stdin to a file errors with ENOTSUP #14054, a different defect, and this PR does not change it.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I re-reviewed after 79a1f99 and found no new bugs; the second commit's move of the wait into shared read_retrying/write_retrying (dropping wait_for_kernel_copy) and the {"exit":$?} stage in the test pipeline address two of my earlier inline threads. A human look is still worthwhile because the remaining open threads are design trade-offs (an unbounded wait on a pool thread where the base rejected at once), and the macOS select$DARWIN_EXTSN$NOCANCEL path first runs on CI.

What was reviewed:

  • The new EAGAIN arm in do_copy_file_range: remain is already decremented for a known size and total_written is passed by reference, so the fallback loop continues from the byte sendfile/splice stopped at; CopyFileRange is excluded, so two regular files never wait.
  • read_retrying/write_retrying: on a failed wait they return the original EAGAIN, and is_retry() is a plain errno == EAGAIN check, so no other errno is retried.
  • The macOS wait_until: EINTR is retried, the inline set is FD_SETSIZE/32 words with a heap Vec above that, and the *mut u32 extern matches the DARWIN_EXTSN bit-array ABI — but this is #[cfg(target_os = "macos")] and could not be compiled or run here.
  • write_all_to_stdio refactor is behavior-preserving (same poll(-1) on POLLOUT via the new helper).
Extended reasoning...

The PR touches the bun_sys syscall layer (new poll/select wait helpers and a new select$DARWIN_EXTSN$NOCANCEL extern on macOS), the Linux sendfile/splice copy loop in copy_file.rs, the node:fs read/write copy loop, and adds a large describe.concurrent block of shell-pipeline subprocess tests; it touches no auth, crypto, or injection surface. Since my prior review the second commit replaced the per-syscall poll-and-retry with a fallback to the shared read/write loop, which removes the POLLERR retry logic I flagged, and the tests now assert the Bun child's own exit status. No new findings surfaced this run (exit reason dry_streak) and none of the changed files are under CODEOWNERS. Deferring rather than approving because the change converts a fast EAGAIN rejection into an unbounded park of a work-pool thread (an acknowledged, still-open design trade-off), and the platform-gated macOS select path cannot be type-checked or exercised from this Linux checkout.

Still open from earlier reviews (2):

  • Unresolved: 1 minor or pre-existing, 1 blocking on lines changed since (possibly already fixed).

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

@robobun

robobun commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

The re-review of 79a1f99 has no new findings. Two earlier threads were still open. Both are design trade-offs that this PR does not change in code, so I resolved them. The Downsides section has both, with numbers.

The replies in the two threads have the measurements. The notes in the description have a question for the maintainer about the pool thread.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant